#ifndef GROUP_H
#define GROUP_H

//Standard library headers
#include <iostream>

// Purpose: Provide a generic grouping type that supports the boolean logic
//          operators (&, |, ^, ~) as per the theory of sets in order to permit
//          such constructs as resultGroup = (blueGroup & ~hotGroup) ^ bigGroup;

// Status:  Still being thought-out; nowhere near complete.

//TODO:     Decide if should inherit or contain the base container object.
//          Decide whether the base container object should (always) be a map.
//          Use the standard library algorithms (includes, set_union,
//          set_intersection, set_difference, set_symmetric_difference) to
//          implement the desired operators.

template< class T >
class Group : public std::vector<T>
{
public:
	
	/*friend std::istream& operator >> (std::istream &isInput, Vector3D& obj);*/
	//friend std::ostream& operator << (std::ostream &osResult, const Vector3D& obj);

private:
    
    typedef std::vector<T>::iterator GroupIterator;
    
public:
	
	//Initalisation constructor
	//Group( std::vector<T>& given ) { V[0]=X; V[1]=Y; V[2]=Z; }
	
	//TODO: This class was modified from Vector3D, so that's why there are still
	//      lots of strange bits.
	
	//Array access operator
	scalar operator [] (const unsigned i) const	{ return V[i]; }
	scalar &operator [] (const unsigned i) { return V[i]; }
	
	//Copy constructor
	Vector3D(const Vector3D &vec) { V[0]=vec[0]; V[1]=vec[1]; V[2]=vec[2]; }
	
	//Function to return vector array
	scalar* getArray()					{ return V; }
	
	//Element access functions
	//TODO: Could replace these with a union?
	//      Perhaps could have many different names as well, e.g., (i, j, k), (x, y, z) etc.
	scalar x()	const				{ return V[0]; }
	scalar y()	const				{ return V[1]; }
	scalar z()	const				{ return V[2]; }
	scalar &x()						{ return V[0]; }
	scalar &y()						{ return V[1]; }
	scalar &z()						{ return V[2]; }
	
	//friend std::ostream& operator <<(std::ostream &os,const Vector3D &obj);
	
	//Vector assignment
	const Vector3D &operator = (const Vector3D &vec)
	{
		V[0] = vec[0];
		V[1] = vec[1];
		V[2] = vec[2];
		
		return *this;
	}
	
	//Scalar assignment
	//TODO: Decide if this should be allowed
	const Vector3D &operator = (const scalar &s)
	{
		V[0] = s;
		V[1] = s;
		V[2] = s;
		
		return *this;
	}
	
	//Vector equality
	const bool operator == (const Vector3D &vec) const
	{
	    return ((V[0] == vec[0]) && (V[1] == vec[1]) && (V[2] == vec[2]));
	}
	
	//Vector inequality
	const bool operator != (const Vector3D &vec) const
	{
	    return !(*this == vec);
	}
	
	//Vector add
	const Vector3D operator + (const Vector3D &vec) const
	{
 	    return Vector3D(V[0] + vec[0], V[1] + vec[1], V[2] + vec[2]);
	}
	
	//Vector add (opposite of negation)
	const Vector3D operator + () const
	{    
	    return Vector3D(*this);
	}
	
	//Vector increment
	const Vector3D& operator += (const Vector3D& vec)
	{    
 	    V[0] += vec[0];
	  	V[1] += vec[1];
	  	V[2] += vec[2];
	  	
	  	return *this;
	}
	
	//Vector subtraction
	const Vector3D operator - (const Vector3D& vec) const
	{    
	    return Vector3D(V[0] - vec[0], V[1] - vec[1], V[2] - vec[2]);
	}
	
	//Vector negation
	const Vector3D operator - () const
	{    
	   return Vector3D(-V[0], -V[1], -V[2]);
	}
	
	//Vector decrement
	const Vector3D &operator -= (const Vector3D& vec)
	{
	    V[0] -= vec[0];
	    V[1] -= vec[1];
	    V[2] -= vec[2];
	
	    return *this;
	}
	
	//scalar self-multiply
	const Vector3D &operator *= (const scalar &s)
	{
	    V[0] *= s;
	    V[1] *= s;
	    V[2] *= s;
	  
	    return *this;
	}
	
	//Scalar self-divide
	const Vector3D &operator /= (const scalar &s)
	{
	    const scalar recip = 1/s; // for speed, one divecision
	
	    V[0] *= recip;
	    V[1] *= recip;
	    V[2] *= recip;
	
	    return *this;
	}
	
	//Or
	const Group<T> operator | (const Group<T>& other) const
	{
	    Group<T> result;
		Group<T>* pBiggestGroup = ( this->size() > other.size() ) ? this : &other;
		Group<T>::iterator itElement;
		
		//for ( itElement = this->begin(); itElement != this->end(); ++itElement )
			//result.
		//	if ( itElement
	}
	
	//Xor ("or but not both")
	const Group<T> operator ^ (const Group<T>& other) const
	{
	    Group<T>* pBiggestGroup = ( this->size() > other.size() ) ? this : &other;
		Group<T>::iterator itElement;
		//for ( itElement = this->begin(); itElement != this->end(); ++itElement )
		//	if ( itElement
	}
	
};

//Stream operators
//----------------
/*inline std::istream& operator >> (std::istream &isInput, Vector3D& obj)
{
	//TODO: Consider implementing this.
	return isInput;
}*/
//NOTE: The inline here is actually necessary to prevent linker errors
inline std::ostream& operator << (std::ostream &osResult, const Vector3D& obj)
{
	osResult << "("  << obj.V[0]
	         << ", " << obj.V[1]
	         << ", " << obj.V[2]
	         << ")"  << std::endl;
	return osResult;
}
//----------------

#endif //GROUP_H
